﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Shapes;

namespace WD.Shell.DrawingObjects
{
    public class CustomLine
    {
        private double _xmax = 6.5;
        private double _ymax = 1.1;
        private readonly Polyline _pl;
        private readonly Canvas _graphControl;

        public Thickness Margin { get; set; }

        public CustomLine(Canvas graphControl)
        {
            Ymin = -1.1;
            _graphControl = graphControl;
            _graphControl.SizeChanged += GraphControlSizeChanged;
            Init();
            _pl = new Polyline();
        }

        public bool IsCoSine { get; set; }

        public bool AutoResize { get; set; }

        public SolidColorBrush Stroke { get; set; }

        public List<Point> CurvePoints { get; set; }

        public double StrokeThickness { get; set; }

        public double Width { get; set; }

        public DoubleCollection StrokeDashArray { get; set; }

        public int PointLength { get; set; }

        public int TotalPoint { get; set; }

        public int StartLocation { get; set; }

        public double Xmin { get; set; }

        public double Ymin { get; set; }

        public double Opacity { get; set; }

        public bool ShowEffect { get; set; }

        public Effect LineEffect { get; set; }

        public double Xmax
        {
            get { return _xmax; }
            set { _xmax = value; }
        }

        public double Ymax
        {
            get { return _ymax; }
            set { _ymax = value; }
        }

        #region Private Functions

        private void GraphControlSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (AutoResize)
            {
                CurvePoints.Clear();
                DrawSine();
            }
        }

        private void DrawLine()
        {
            if (IsCoSine == false)
            {
                DrawSine();
            }
            else
            {
                DrawCosine();
            }
        }

        private void Init()
        {
            CurvePoints = new List<Point>();
            Stroke = Brushes.Black;
            Width = GetGraphWidth();
            StrokeThickness = 2;
            StrokeDashArray = null;
            TotalPoint = 1000;
            PointLength = 64;
            StartLocation = 0;
            Xmin = 0.1;
            Opacity = 1;
            ShowEffect = true;
            LineEffect  = new BlurEffect {Radius = 2};
        }

        private void DrawCosine()
        {
            CurvePoints.Clear();

            if (_pl != null)
                _graphControl.Children.Remove(_pl);

            for (var i = StartLocation; i < TotalPoint; i++)
            {
                var x = i / 5.0;
                var y = Math.Cos(x);
                var curvePoint = GetCurvePoint(new Point(x, y));
                CurvePoints.Add(curvePoint);
            }

            if (_pl != null)
            {
                _pl.Width = GetGraphWidth();
                _pl.Stroke = Stroke;
                if (ShowEffect)
                {
                    _pl.Effect = LineEffect;
                }
                _pl.Opacity = Opacity;
                _pl.Margin = Margin;
                _pl.Points = new PointCollection(CurvePoints.Take(PointLength));
                _graphControl.Children.Add(_pl);
            }
        }

        private void DrawSine()
        {
            CurvePoints.Clear();

            if (_pl != null)
                _graphControl.Children.Remove(_pl);

            for (var i = StartLocation; i < TotalPoint; i++)
            {
                var x = i / 5.0;
                var y = Math.Sin(x);
                var curvePoint = GetCurvePoint(new Point(x, y));
                CurvePoints.Add(curvePoint);
            }

            if (_pl != null)
            {
                _pl.Width = GetGraphWidth();
                _pl.Stroke = Stroke;
                if (ShowEffect)
                {
                    _pl.Effect = LineEffect;
                }
                _pl.Opacity = Opacity;
                _pl.Margin = Margin;
                _pl.Points = new PointCollection(CurvePoints.Take(PointLength));
                _graphControl.Children.Add(_pl);
            }
        }

        private Point GetCurvePoint(Point pt)
        {
            var x = (pt.X - Xmin) * GetGraphWidth() / (Xmax - Xmin);
            var y = GetGraphHeight() - (pt.Y - Ymin) * GetGraphHeight() / (Ymax - Ymin);
            return new Point { X = x, Y = y };
        }

        private double GetGraphHeight()
        {
            return _graphControl.ActualHeight -(Margin.Top + Margin.Bottom);
        }

        private double GetGraphWidth()
        {
            return _graphControl.ActualWidth -(Margin.Left + Margin.Right);
        }

        #endregion

        public void Draw()
        {
            DrawLine();
        }
    }
}
